﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.IO;
using System.Text;
using System.Net;
using System.Threading.Tasks;
using QFrameworkOne.Diagnostics;


namespace QFrameworkOne.Module {
    /// <summary>
    /// QHttpWebServer 基于HttpRequest实现的WebServer
    /// </summary>
    public class QHttpWebServer : IQMoudule {
        string IQMoudule.Module_Name => "QHttpWebServer";

        string IQMoudule.Module_Author => "DealiAxy";

        string IQMoudule.Module_Mail => "admin@deali.cn";

        string IQMoudule.Module_Website => "http://blog.deali.cn";

        string IQMoudule.Module_Description => "QHttpWebServer 基于HttpRequest实现的WebServer";

        QComponentVersion IQMoudule.Module_Version => new QComponentVersion() {
            Major = 1,
            Minor = 0,
            Update = 16,
            Label = QComponentVersionLabel.Alpha
        };

        /// <summary>
        /// 获取HttpListenerRequest事件
        /// </summary>
        public event Action<HttpListenerRequest, HttpListenerResponse> OnGetRequest;

        /// <summary>
        /// 获取原始数据事件
        /// </summary>
        public event Action<HttpListenerContext> OnGetRawContext;

        /// <summary>
        /// 
        /// </summary>
        public event Action<string, HttpListenerResponse> OnHttpGet;

        /// <summary>
        /// 
        /// </summary>
        public event Action<string, HttpListenerResponse> OnHttpPost;

        /// <summary>
        /// 服务器启动事件
        /// </summary>
        public event Action<HttpListener> OnServerStart;

        /// <summary>
        /// 服务器停止事件
        /// </summary>
        public event Action<HttpListener> OnServerStop;

        /// <summary>
        /// 服务器监听端口，只读
        /// </summary>
        public int Port => port;

        /// <summary>
        /// 网站根目录
        /// </summary>
        public string WebRoot = "";

        /// <summary>
        /// 是否开启PHP功能
        /// </summary>
        public bool PHP_CGI_Enabled = true;

        /// <summary>
        /// PHP执行文件路径
        /// </summary>
        public string PHP_CGI_Path = "php-cgi";

        /// <summary>
        /// MIME类型
        /// </summary>
        public Dictionary<string, string> MIME_Type = new Dictionary<string, string>() {
            //{ "extension", "content type" }
            {"htm", "text/html"},
            {"html", "text/html"},
            {"php", "text/html"},
            {"xml", "text/xml"},
            {"json", "application/json"},
            {"txt", "text/plain"},
            {"js", "application/x-javascript"},
            {"css", "text/css"},
            {"bmp", "image/bmp"},
            {"ico", "image/ico"},
            {"png", "image/png"},
            {"gif", "image/gif"},
            {"jpg", "image/jpeg"},
            {"jpeg", "image/jpeg"},
            {"webp", "image/webp"},
            {"zip", "application/zip"},
            {"*", "*/*"}
        };

        private QDebug Qdb = new QDebug() {
            CodeLocation = "QFrameworkOne.Module.QHttpWebServer",
            ConsoleOutput = true,
            FileOutput = false
        };

        private int port;
        private HttpListener httpListener;

        /// <summary>
        /// 构造函数，需要制定监听端口
        /// </summary>
        /// <param name="listenPort">监听端口</param>
        public QHttpWebServer(int listenPort) {
            port = listenPort;
            httpListener = new HttpListener();
        }

        /// <summary>
        /// 开启服务器
        /// </summary>
        /// <returns>是否成功开启服务器</returns>
        public bool Start() {
            //触发事件
            //if (OnServerStart != null)
            //  OnServerStart(httpListener);
            //以下是简化后的代码
            OnServerStart?.Invoke(httpListener);

            try {
                //监听端口
                httpListener.Prefixes.Add("http://+:" + port.ToString() + "/");
                httpListener.Start();
                httpListener.BeginGetContext(new AsyncCallback(OnResponse), httpListener); //开始异步接收request请求
            }
            catch (Exception ex) {
                Qdb.Error(ex.Message, QDebugErrorType.Error, "Start");
                return false;
            }

            return true;
        }

        /// <summary>
        /// 启动本地网页服务器
        /// </summary>
        /// <param name="webroot">网站根目录</param>
        /// <returns></returns>
        public bool Start(string webroot) {
            //触发事件
            OnServerStart?.Invoke(httpListener);

            WebRoot = webroot;
            try {
                //监听端口
                httpListener.Prefixes.Add("http://+:" + port.ToString() + "/");
                httpListener.Start();
                httpListener.BeginGetContext(new AsyncCallback(OnWebResponse), httpListener); //开始异步接收request请求
            }
            catch (Exception ex) {
                Qdb.Error(ex.Message, QDebugErrorType.Error, "Start");
                return false;
            }

            return true;
        }

        /// <summary>
        /// 停止服务器
        /// </summary>
        public void Stop() {
            //触发事件
            OnServerStop?.Invoke(httpListener);

            httpListener.Stop();
        }

        /// <summary>
        /// 网页服务器相应处理
        /// </summary>
        /// <param name="ar"></param>
        private void OnWebResponse(IAsyncResult ar) {
            byte[] responseByte = null; //响应数据

            HttpListener httpListener = ar.AsyncState as HttpListener;
            HttpListenerContext context = httpListener.EndGetContext(ar); //接收到的请求context（一个环境封装体）            

            httpListener.BeginGetContext(new AsyncCallback(OnWebResponse), httpListener); //开始 第二次 异步接收request请求

            //触发事件
            OnGetRawContext?.Invoke(context);

            HttpListenerRequest request = context.Request; //接收的request数据
            HttpListenerResponse response = context.Response; //用来向客户端发送回复

            //触发事件
            OnGetRequest?.Invoke(request, response);

            //处理请求文件名以及参数
            string fileName = "";
            string rawUrl = request.RawUrl;
            //控文件名情况的处理
            Func<string, string> fileNameNullProc = new Func<string, string>((dir) => {
                string[] paths = {
                    WebRoot + @"\" + dir + @"\index.html",
                    WebRoot + @"\" + dir + @"\index.htm",
                    WebRoot + @"\" + dir + @"\index.php",
                    WebRoot + @"\" + dir + @"\home.html",
                    WebRoot + @"\" + dir + @"\home.htm",
                    WebRoot + @"\" + dir + @"\home.php",
                };
                foreach (string s in paths) {
                    if (File.Exists(s))
                        return s;
                }

                return paths[0];
            });

            if (rawUrl == "" || rawUrl == "/") //单纯输入域名或主机IP地址
                fileName = fileNameNullProc("");
            else if (rawUrl.IndexOf('.') == -1) //不带扩展名，理解为文件夹
                fileName = fileNameNullProc(rawUrl.Substring(1));
            else {
                int fileNameEnd = rawUrl.IndexOf('?');
                if (fileNameEnd > -1)
                    fileName = rawUrl.Substring(1, fileNameEnd - 1);

                fileName = WebRoot + @"\" + rawUrl.Substring(1);
            }

            //处理请求文件名的后缀
            string fileExt = Path.GetExtension(fileName).Substring(1);

            //PHP处理
            string phpCgiOutput = "";
            Action phpProc = new Action(() => {
                try {
                    string argStr = "";

                    if (request.HttpMethod == "GET") {
                        if (rawUrl.IndexOf('?') > -1)
                            argStr = rawUrl.Substring(rawUrl.IndexOf('?'));
                    }
                    else if (request.HttpMethod == "POST") {
                        using (StreamReader reader = new StreamReader(request.InputStream)) {
                            argStr = reader.ReadToEnd();
                        }
                    }

                    Process p = new Process();
                    p.StartInfo.CreateNoWindow = false; //不显示窗口
                    p.StartInfo.RedirectStandardOutput = true; //重定向输出
                    p.StartInfo.RedirectStandardInput = false; //重定向输入
                    p.StartInfo.UseShellExecute = false; //是否指定操作系统外壳进程启动程序
                    p.StartInfo.FileName = PHP_CGI_Path;
                    p.StartInfo.Arguments = string.Format("-q -f {0} {1}", fileName, argStr);
                    p.Start();

                    StreamReader sr = p.StandardOutput;
                    while (!sr.EndOfStream) {
                        phpCgiOutput += sr.ReadLine() + Environment.NewLine;
                    }

                    responseByte = sr.CurrentEncoding.GetBytes(phpCgiOutput);
                }
                catch (Exception ex) {
                    Qdb.Error(ex.Message, QDebugErrorType.Error, "onWebResponse->phpProc");
                    response.StatusCode = (int) HttpStatusCode.InternalServerError;
                }
            });

            if (fileExt == "php" && PHP_CGI_Enabled) {
                phpProc();
            }
            else {
                if (!File.Exists(fileName)) {
                    responseByte = Encoding.UTF8.GetBytes("404 Not Found!");
                    response.StatusCode = (int) HttpStatusCode.NotFound;
                }
                else {
                    try {
                        responseByte = File.ReadAllBytes(fileName);
                        response.StatusCode = (int) HttpStatusCode.OK;
                    }
                    catch (Exception ex) {
                        Qdb.Error(ex.Message, QDebugErrorType.Error, "onWebResponse");
                        response.StatusCode = (int) HttpStatusCode.InternalServerError;
                    }
                }
            }

            if (MIME_Type.ContainsKey(fileExt))
                response.ContentType = MIME_Type[fileExt];
            else
                response.ContentType = MIME_Type["*"];

            response.Cookies = request.Cookies; //处理Cookies

            response.ContentEncoding = Encoding.UTF8;

            using (Stream output = response.OutputStream) //发送回复
            {
                try {
                    output.Write(responseByte, 0, responseByte.Length);
                }
                catch (Exception ex) {
                    Qdb.Error(ex.Message, QDebugErrorType.Error, "onWebResponse");
                    response.StatusCode = (int) HttpStatusCode.InternalServerError;
                }
            }
        }

        /// <summary>
        /// 完全自定义的服务器
        /// </summary>
        /// <param name="ar"></param>
        private void OnResponse(IAsyncResult ar) {
            HttpListener httpListener = ar.AsyncState as HttpListener;
            HttpListenerContext context = httpListener.EndGetContext(ar); //接收到的请求context（一个环境封装体）            

            //触发事件
            OnGetRawContext?.Invoke(context);

            httpListener.BeginGetContext(new AsyncCallback(OnResponse), httpListener); //开始 第二次 异步接收request请求

            HttpListenerRequest request = context.Request; //接收的request数据
            HttpListenerResponse response = context.Response; //用来向客户端发送回复

            //触发事件
            OnGetRequest?.Invoke(request, response);

            switch (request.HttpMethod.ToLower()) {
                case "get":
                    string rawUrl = request.RawUrl;
                    rawUrl = rawUrl.Substring(2);

                    //触发事件
                    OnHttpGet?.Invoke(rawUrl, response);

                    break;
                case "post":
                    string inputData = "";
                    using (StreamReader reader = new StreamReader(request.InputStream)) {
                        inputData = reader.ReadToEnd();
                    }

                    //触发事件
                    OnHttpPost?.Invoke(inputData, response);

                    break;
            }
        }
    }
}